Encabezado.jpg

APLICACIΓ“N DE ALGORITMOS DE SEGMENTACIΓ“N Y CLÚSTER EN IMÁGENES AGRÍCOLAS, MEDIANTE EL USO DE HERRAMIENTAS DE COMPUTO
ΒΆ

MODULO 1: OPERACIONES BASICAS
AUTOR: LOPEZ ESTEBAN MIGUEL
FECHA: 01 DE JULIO 2024

El presente mΓ³dulo se centra en el manejo bΓ‘sico de imΓ‘genes, proporcionando una base sΓ³lida para entender las operaciones fundamentales. Estas operaciones son cruciales para la preparaciΓ³n y el preprocesamiento adecuados de las imΓ‘genes, asegurando que los algoritmos mΓ‘s complejos puedan funcionar de manera eficiente. Este mΓ³dulo servirΓ‘ como una revisiΓ³n bibliogrΓ‘fica de las operaciones bΓ‘sicas, ofreciendo explicaciones teΓ³ricas y ejemplos prΓ‘cticos del funcionamiento de cada una.

INDICEΒΆ

  1. librerias
    1.1. OpenCV
    1.1.1. Importacion
    1.2. Matplotlib
    1.2.1. Importacion
    1.3. Numpy
    1.3.1. Importacion
  2. Lectura de imagenes
    2.1. ejemplo
  3. Visualizacion de imagenes
    3.1 visualizacion con OpenCV
    3.2.Visualizacion con matplotlib
    3.3. Diferencias entre OpenCV y Matplotli
  4. imagen BGR a RGB
  5. Propiedades de una imagen
    5.1. Dimensiones de una imagen
    5.1.1. Ejemplo
    5.2. Altura, ancho y numero de canales en una imagen
    5.3. Numero de pixeles en una imagen
    5.4. Tipos de datos en una imagen
    5.4.1. Datos uint8
    5.4.2. Datos uint16
    5.4.3. Datos float32
    5.4.4. Ejemplo
    5.5. TamaΓ±o de una imagen
  6. Redimensionamiento de imagenes
  7. Canales de una imagen RGB
    7.1. Separar los canales de una imagen RGB
    7.2. Creacion de imagenes
    7.3. Fusion de canales
    7.3.1. Apagar canales en una imagen
    7.3.2. Modificar canales
  8. Estadisticas de pixeles
    8.1. Minimo y maximo en pixeles
    8.2. Media en pixeles
    8.3. Desviacion estandar en pixeles
  9. Acceso y manipulacion de pixeles
    9.1. Valores RGB en los pixeles
    9.2. Manipulacion de pixeles en un rango (ROI)
    9.3. manipulacion de todos los pixeles
    9.3.1. Inversion de colores
    9.3.2. Imagenes en escala de grises
  10. Referencias

librerΓ­as
ΒΆ

En este mΓ³dulo utilizaremos tres librerΓ­as bΓ‘sicas para el procesamiento de imΓ‘genes en Python: OpenCV, Matplotlib y NumPy. A continuaciΓ³n, se describen estas librerΓ­as, su utilidad en el contexto del procesamiento de imΓ‘genes, y cΓ³mo instalarlas.

OPENCV
ΒΆ

OpenCV (Open Source Computer VisiΓ³n Library) es una biblioteca de cΓ³digo abierto que contiene diferentes algoritmos optimizados para el procesamiento de imΓ‘genes. Esta librerΓ­a proporciona herramientas para leer, manipular y procesar imΓ‘genes. Es una de las herramientas mΓ‘s utilizadas para este propΓ³sito y estarΓ‘ presente en casi todos los procesos en los que trabajemos con imΓ‘genes en este mΓ³dulo.
ImportaciΓ³n
ΒΆ
InΒ [Β ]:
import cv2

MATPLOTLIB
ΒΆ

Matplotlib es una librerΓ­a de visualizaciΓ³n que es ΓΊtil para crear grΓ‘ficos y figuras interactivas. En este mΓ³dulo, utilizaremos Matplotlib para visualizar las imΓ‘genes con las que trabajamos sin la necesidad de abrir una ventana emergente, lo que facilita la presentaciΓ³n de los resultados en notebooks.
ImportaciΓ³n
ΒΆ
InΒ [Β ]:
import matplotlib.pyplot as plt

NUMPY
ΒΆ

NumPy es una librerΓ­a que sirve como soporte para trabajar con matrices, cuya importancia radica en que las imΓ‘genes son, en esencia, arreglos de matrices. Dado que estaremos trabajando con imΓ‘genes, esta librerΓ­a nos permite optimizar nuestras operaciones con las matrices de nuestras imΓ‘genes de manera que nuestro cΓ³digo sea mΓ‘s eficiente.
ImportaciΓ³n
ΒΆ
InΒ [Β ]:
import numpy as np

Lectura de imΓ‘genes
ΒΆ

Para cargar imΓ‘genes en OpenCV, utilizamos la funciΓ³n imread de la librerΓ­a cv2. Esta funciΓ³n recibe como parΓ‘metro una ruta, que es la ubicaciΓ³n de la imagen en el sistema. La ruta debe incluir el nombre de la imagen y su extensiΓ³n, esta ruta debe escribirse entre comillas.
Ejemplo
ΒΆ
Para este modulo leeremos nuestra imagen llamada fcolores.jpg la cual se encuentra en la misma carpeta de nuestro script de Python. Podemos leer nuestra imagen de la siguiente manera:
InΒ [Β ]:
imagen = cv2.imread('fcolores.jpg')
En este ejemplo,imagen es una variable que almacenara la imagen cargada.

VisualizaciΓ³n de imΓ‘genes
ΒΆ

Una vez que la imagen ha sido leΓ­da y cargada, el siguiente paso es visualizarla, para observar que se ha cargado de manera adecuada. Este paso es crucial ya que podemos observar nuestra imagen y en caso de estar llevando la imagen por un tratamiento, podemos ver los cambios que sufre nuestra imagen con nuestras operaciones, de esta manera podemos saber si nos estamos acercando a nuestro objetivo con la imagen.

VisualizaciΓ³n con OpenCV
ΒΆ

La visualizaciΓ³n de imΓ‘genes con OpenCV es sencilla. Solo necesitamos usar la funciΓ³n cv2.imshow(), y en los parΓ‘metros, colocar el tΓ­tulo que le pondremos a la imagen y la imagen que vamos a mostrar.

DespuΓ©s colocamos cv2.waitkey(), la cual sirve como una pausa para que podamos visualizar la imagen sin que se cierre repentinamente.

Por ΓΊltimo, cv2.destroyallwindows() cierra todas las ventanas que se hallan abierto.

InΒ [Β ]:
#mostramos la imagen que ya cargamos en la variable imagen
cv2.imshow('imagen mostrada con opencv',imagen)
cv2.waitKey()
cv2.destroyAllWindows()

image.png

VisualizaciΓ³n con Matplotlib
ΒΆ

Para visualizar las imΓ‘genes con Matplotlib haremos uso de la funciΓ³n plt.imshow(), en la cual solo necesitamos agregar como parΓ‘metro la imagen que vamos a visualizar.

En caso de querer agregarle un tΓ­tulo a nuestra imagen podemos usar plt.title() , en la cual solo agregaremos el titulo que queramos agregarle a nuestra imagen, recordar que al ser un string este debe colocarse entre comillas.

Normalmente Matplotlib es usado para hacer graficas por lo cual siempre se muestra con unos ejes (x , y), los cuales son ΓΊtiles muchas veces para ver las dimensiones de nuestra imagen, sin embargo, en este caso no los ocupamos por lo cual desactivaremos los ejes mediante la funciΓ³n plt.axis(β€˜off’), si queremos observar los ejes simplemente omitimos el llamado de esta funciΓ³n.

Por ΓΊltimo, hacemos uso de la funciΓ³n plt.show(), la cual nos mostrara la imagen sin que se cierre.

InΒ [Β ]:
#mostramos la imagen que cargamos en la variable imagen
plt.imshow(imagen)
plt.title('Imagen mostrada con Matplotlib')
plt.axis('off')
plt.show()
No description has been provided for this image

Diferencias entre OpenCV y Matplotlib
ΒΆ

Podemos ver que nuestra imagen se ve diferente al mostrarla con OpenCV y Matplotlib. Esto se debe a la manera como cada biblioteca lee las imΓ‘genes.

La imagen con la que estamos trabajando tiene 3 canales (rojo, verde, azul). OpenCV lee las imΓ‘genes en el siguiente orden: azul, verde, rojo. Este orden se llama BGR. Sin embargo, Matplotlib lee las imΓ‘genes en el siguiente orden. Rojo, verde, azul. Este orden se llama RGB.

Como la imagen la hemos cargado con OpenCV, esta en el orden BGR, por lo que al leerla con la librerΓ­a de Matplotlib, interpreta la imagen como si fuera RGB. Estos nos muestran una imagen con colores diferentes a los normales. Este no es un gran problema ya que en el siguiente tema se corregirΓ‘ la imagen.

Imagen BGR a RGB
ΒΆ

Como notamos en el tema anterior, la visualizaciΓ³n de la imagen con Matplotlib se realiza de manera incorrecta debido a la errΓ³nea lectura de la imagen. Para corregir este aspecto, vamos a intercambiar los canales de la imagen con la funciΓ³n cv2.cvtColor, que permite convertir la imagen a diferentes espacios de color.
InΒ [Β ]:
#convertimos la imagen a RGB
imagen_rgb = cv2.cvtColor(imagen,cv2.COLOR_BGR2RGB)

#mostramos la imagen RGB
plt.imshow(imagen_rgb)
plt.title('Imagen RGB')
plt.axis('off')
plt.show()
No description has been provided for this image
En este caso, imagen es la variable que contiene la imagen cargada originalmente en formato BGR. La funciΓ³n cv2.cvtColor convierte esta imagen a formato RGB y la almacena en la variable imagen_rgbi. Luego con Matplotlib mostramos la imagen.

Ahora podemos observar como la imagen se muestra con sus colores correctos, lo cual nos indica que hemos intercambiado los canales de manera correcta.

Propiedades de una imagen
ΒΆ

Una imagen tiene consigo diferentes propiedades, a continuaciΓ³n vamos a desglosar como podemos hallar mediante programaciΓ³n algunas de ellas, y saber que es lo que nos quiere decir cada una.

Dimensiones de una imagen
ΒΆ

Para saber que significan el nΓΊmero de dimensiones en nuestra imagen, debemos tener en claro que nuestra imagen es un arreglo de matrices. Estas matrices tienen una altura y un ancho, lo cual indica que nuestra imagen tiene 2 dimensiones. Esto solo sucede cuando nuestra imagen tiene un solo canal. En el caso de las imΓ‘genes RGB, estas tienen 3 canales, por ende, nuestra imagen tiene alto, ancho y profundidad.

Las dimensiones de una imagen se refieren a su estructura en tΓ©rminos de filas y columnas de pixeles. En tΓ©rminos mΓ‘s tΓ©cnicos, una imagen digital es una matriz bidimensional si tiene un solo canal (por ejemplo, una imagen en escala de grises). Si la imagen tiene mΓΊltiples canales (por ejemplo, una imagen RGB), se convierte en una matriz tridimensional.

  1. Alto (height): nΓΊmero de filas de pixeles en la imagen.
  2. Ancho (width): numero de columnas de pixeles en la imagen.
  3. Profundidad (Depth): numero de canales de color en la imagen (por ejemplo, 3 para una imagen RGB)
Ejemplo
ΒΆ
Trabajaremos con la variable imagen_rgb que ya usamos anteriormente. Para hallar las dimensiones de esta imagen usaremos la propiedad ndim de numpy, la cual nos da el numero de dimensiones de la imagen.
InΒ [Β ]:
#usamos la propiedad ndim para saber cuantas dimensiones tiene nuestra imagen
dimensiones = imagen_rgb.ndim

#mostramos el resultado en consola
print(f'Nuestra imagen tiene {dimensiones} dimensiones')
Nuestra imagen tiene 3 dimensiones
Podemos observar que las dimensiones son 3, lo cual indica que nuestra imagen tiene 3 dimensiones: alto, ancho y profundidad (canales).

En el caso de que nuestra imagen solo mostrara 2 dimensiones, nos indicarΓ­a que nuestra imagen esta en escala de grises, o es monocromΓ‘tica, es decir solo tendrΓ­a filas y columnas de pixeles, pero no contarΓ­a con color.

Conocer el numero de dimensiones de una imagen es crucial para realizar operaciones de procesamiento de imΓ‘genes de manera adecuada. Muchas funciones y algoritmos dependen de las dimensiones de la imagen para funcionar correctamente. Por ejemplo, aplicar filtros, realizar transformaciones de color o segmentar la imagen requiere conocimiento preciso de la estructura dimensional.

Altura, ancho y numero de canales en una imagen
ΒΆ

Como se observΓ³ en el punto anterior nuestra imagen consta de 3 dimensiones, en este apartado vamos a descubrir los valores de cada una de ellas. Para hacer esto haremos uso de la propieda shape , la cual arroja los valores de estos datos de nuestra imagen.
InΒ [Β ]:
#almacenamos los valores del tamaΓ±o, ancho y numero de canales 
alto, ancho, ncanales = imagen_rgb.shape

#imprimimos los valores de nuestra imagen 
print(f'Nuestra imagen tiene {alto} pixeles de alto, {ancho} pixeles de ancho y consta de {ncanales} canales')
Nuestra imagen tiene 496 pixeles de alto, 618 pixeles de ancho y consta de 3 canales

Numero de pixeles en una imagen
ΒΆ

Teniendo en cuenta el alto y ancho de nuestra imagen podemos calcular el nΓΊmero de pixeles que conforman a nuestra imagen mediante la siguiente operaciΓ³n:

$$npixeles=alto*ancho$$

De esta manera hallaremos el numero de pixeles que conforman a nuestra imagen.

InΒ [Β ]:
npixeles = alto * ancho
print(f'Nuestra imagen esta conformada por {npixeles} pixeles')
Nuestra imagen esta conformada por 306528 pixeles

Tipos de datos en una imagen
ΒΆ

Cada pΓ­xel en una imagen esta definido en un rango de valor. Estos rangos definen el tipo de datos que conforman a la imagen y son cruciales debido a que son los encargados de almacenar y manipular los datos en la memoria. Generalmente, encontramos 3 tipos de datos.
  1. β€˜uint8’
  2. β€˜uint16’
  3. β€˜float32’
Datos β€˜uint8’
ΒΆ
El dato uint8 es generalmente el mΓ‘s usado, ya que es el que contine la mayorΓ­a de las imΓ‘genes, debido a que contiene valores de 0 a 256, es comΓΊn usarlo en imΓ‘genes que se encuentran en escala de grises o en imΓ‘genes RGB.

Este tipo de dato ocupa muy poco espacio en la memoria, llegando a ocupar solamente 1 byte por pΓ­xel, lo cual nos permite un procesamiento rΓ‘pido, en el caso de nuestra imagen al estar en RGB tendrΓ‘ 1 valor uint8 por cada canal de la imagen, por lo que cada pixel de nuestra imagen constarΓ‘ de 3 valores de este tipo.

Datos β€˜uint16’
ΒΆ
Los valores uint16 son datos un poco mΓ‘s pesados que contienen un rango de 0 a 65,535, por lo cual estamos hablando de imΓ‘genes con una alta profundidad de color, este tipo de dato es ΓΊtil en imΓ‘genes que requieren cierto grado de detalle.
Datos β€˜float32’
ΒΆ
Los datos de tipo float32 son imΓ‘genes muy avanzadas que comprenden valores de -3.4e38 a 3.4e38, lo cual los hace ΓΊtil en imΓ‘genes mΓ©dicas o cientΓ­ficas, por lo cual este tipo de datos permite una precisiΓ³n demasiada alta al momento de realizar operaciones con estas, sin embargo, el espacio en memoria de este tipo de imΓ‘genes es muy alto.
Ejemplo
ΒΆ
Para hallar el tipo de dato de nuestra imagen usamos la propiedad dtype, la cual nos arroja el tipo de datos del que se compone nuestra imagen.
InΒ [Β ]:
#hallamos el tipo de dato del que se compone nuestra imagen (imagen_rgb)
datotype = imagen_rgb.dtype

#mostramos el resultado
print(f'La imagen esta formado por pixeles de tipo: {datotype}')
La imagen esta formado por pixeles de tipo: uint8

TamaΓ±o de una imagen
ΒΆ

Un dato fundamental de las imΓ‘genes es su tamaΓ±o, es decir la cantidad de memoria que ocupa la imagen en el sistema. Este dato es fΓ‘cil de calcular a partir de otros datos ya recopilados. Para poder calcular este aspecto, debemos tener conocimiento del nΓΊmero de pixeles que tiene nuestra imagen, el nΓΊmero de canales de esta y el tipo de datos que maneja. Con estos datos, simplemente hacemos la siguiente operaciΓ³n:

$$TamaΓ±o = npixeles * canales de la imagen * tipo de dato$$

Hasta el momento sabemos que el nΓΊmero de pixeles se obtiene al multiplicar el alto por el ancho de la imagen, este dato lo multiplicamos por el nΓΊmero de canales que contenga nuestra imagen. En este punto tenemos el total de elementos que conforman nuestra imagen, pero cada uno de estos elementos ocupan un espacio en memoria, cuando manejamos datos uint8 estos datos ocupan solamente 1 byte de memoria, por lo que multiplicamos lo que llevΓ‘bamos por 1 y obtenemos el espacio de memoria que ocupa nuestra imagen.

Sin embrago podemos simplificar este proceso con la funciΓ³n nbytes la cual nos mostrara el resultado de la operaciΓ³n explicada anteriormente, lo cual se muestra en el siguiente ejemplo.

InΒ [Β ]:
#hallamos el tamaΓ±o en bytes de nuestra imagen 
tamaΓ±o_bytes = imagen_rgb.nbytes

#mostramos el resultado
print(f'La imagen tiene un tamaΓ±o de {tamaΓ±o_bytes} bytes')
La imagen tiene un tamaΓ±o de 919584 bytes

REDIMENCIONAMIENTO DE IMAGENES
ΒΆ

El redimensionamiento es una parte crucial del tratamiento de imΓ‘genes, este paso nos permite reducir o ampliar el tamaΓ±o de una imagen de manera que hagamos mΓ‘s fΓ‘cil el trabajar con estas. Al disminuir las medidas de una imagen podemos aumentar la rapidez de procesamiento, lo cual nos permite ser mΓ‘s productivos en nuestros programas.

En este caso reduciremos el tamaΓ±o de la imagen con la que venimos trabajando, de tal manera que dividiremos las dimensiones de nuestra imagen en 2, para esto ocuparemos el mΓ³dulo // que nos permitirΓ‘ darle valores enteros a nuestra imagen, ya que para redimensionarla no podemos ocupar valores con punto decimal.

Una vez que tengamos las nuevas dimensiones de nuestra imagen, pasaremos este parΓ‘metro a la funciΓ³n resize, la cual modificara el tamaΓ±o de nuestra imagen, el funcionamiento de esta funciΓ³n queda explicado en el siguiente ejemplo.

InΒ [Β ]:
#creamos una variable donde almacenamos los nuevos tamaΓ±os de nuestra imagen 
new_tamaΓ±o = (ancho//2,alto//2)

#usamos la funcion de cv2.resize para redimensionar nuestra imagen, recibe como parametros la imagen y el nuevo tamaΓ±o
imagen_redimensionada = cv2.resize(imagen_rgb,new_tamaΓ±o)

#mostramos la imagen redimensionada
plt.imshow(imagen_redimensionada)
plt.title('Imagen redimensionada')
plt.axis('off')
plt.show()
No description has been provided for this image
En este caso no notamos un cambio en la imagen mostrada, esto se debe a que Matplotlib ajusta la imagen de manera automΓ‘tica a la ventana, por lo cual vemos la imagen del mismo tamaΓ±o, sin embargo, observaremos sus propiedades para ver que propiedades de la imagen han sufrido cambios.
InΒ [Β ]:
#calculamos las propiedades de la imagen redimensionada
#dimensiones de la imagen 
dimensiones_new = imagen_redimensionada.ndim
# alto ancho y canales 
alto_new, ancho_new,ncanales_new = imagen_redimensionada.shape
#tipo de datos
datotype_new = imagen_redimensionada.dtype
#numero de pixeles 
npixeles_new = alto_new*ancho_new
#tamaΓ±o en bytes 
tamaΓ±o_bytes_new = imagen_redimensionada.nbytes

#mostramos los cambios en las propiedades 
print(f'Dimensiones       normal: {dimensiones}     redimensionado:{dimensiones_new}')
print(f'Alto              normal: {alto}            redimensionado:{alto_new}')
print(f'Ancho             normal: {ancho}           redimensionado:{ancho_new}')
print(f'canales           normal: {ncanales}        redimensionado:{ncanales_new}')
print(f'tipo de dato      normal: {datotype}        redimensionado:{datotype_new}')
print(f'num pixeles       normal: {npixeles}        redimensionado:{npixeles_new}')
print(f'TamaΓ±o            normal: {tamaΓ±o_bytes}    redimensionado:{tamaΓ±o_bytes_new}')
Dimensiones       normal: 3     redimensionado:3
Alto              normal: 496            redimensionado:248
Ancho             normal: 618           redimensionado:309
canales           normal: 3        redimensionado:3
tipo de dato      normal: uint8        redimensionado:uint8
num pixeles       normal: 306528        redimensionado:76632
TamaΓ±o            normal: 919584    redimensionado:229896
Como observamos solo algunas propiedades de nuestra imagen han cambiado, vamos a desglosar cada propiedad.
  1. Nuestras dimensiones siguen siendo las mismas, ya que nuestra imagen sigue teniendo alto, ancho y un numero de canales.
  2. El alto de nuestra imagen ha cambiado dado que directamente modificamos esta propiedad.
  3. Al igual que el alto el ancho ha cambiado debido a que fue el atributo que modificamos.
  4. El nΓΊmero de canales sigue igual dado que no hicimos cambios en este aspecto.
  5. Nuestra imagen sigue manteniendo el mismo tipo de dato uint8.
  6. Dado que redujimos la altura y ancho de nuestra imagen, el nΓΊmero de pixeles de nuestra imagen ha cambiado, y se ha reducido respecto a la imagen original, y esto se debe a que la imagen ahora es mΓ‘s pequeΓ±a, por lo cual la imagen tiene menos pixeles.
  7. Debido a que nuestra imagen tiene menos pixeles el tamaΓ±o de nuestra imagen ha reducido su tamaΓ±o, por lo cual podemos comprobar que redimensionar una imagen nos ayuda a disminuir el espacio de memoria que ocupa una imagen, lo cual nos ayuda a poder hacer procesamientos mΓ‘s rΓ‘pidos.

Canales de una imagen RGB
ΒΆ

Como hemos explicado anteriormente una imagen es un arreglo de matrices, en este caso un canal es una matriz bidimensional que contiene informaciΓ³n sobre un color en cada pixel de una imagen, si la imagen estΓ‘ constituida por un solo canal podemos observar una imagen gris, sin embargo cuando sobreponemos las matrices de cada canal obtenemos una imagen con color, en este proyecto trabajaremos con imΓ‘genes RGB , es decir imagen con 3 canales, uno rojo, uno verde y uno azul, los cuales al sobreponerse entre sΓ­ generan diferentes tonalidades que representan una imagen clΓ‘sica que podemos observar en cualquier lugar.

Separar los canales de una imagen RGB
ΒΆ

En Python podemos separar los canales de una imagen para poder analizar cada canal por separado, la manera de hacer esto se muestra a continuaciΓ³n.
InΒ [Β ]:
#separamos los canales de nuestra imagen en cada variable almacenaremos un canal
r,g,b = cv2.split(imagen_redimensionada)

#mostramos los canales y la imagen original
fig, axs = plt.subplots(1,4,figsize=(20,6))

#imagen original
axs[0].imshow(imagen_redimensionada)
axs[0].set_title('Imagen original',fontsize=20)
axs[0].axis('off')

#canal rojo
axs[1].imshow(r,cmap ='gray')
axs[1].set_title('Canal rojo',fontsize=20)
axs[1].axis('off')

#canal verde 
axs[2].imshow(g, cmap ='gray')
axs[2].set_title('Canal verde',fontsize=20)
axs[2].axis('off')

#canal azul
axs[3].imshow(b, cmap ='gray')
axs[3].set_title('Canal azul',fontsize=20)
axs[3].axis('off')

#titulo y acomodo de las imagenes
plt.suptitle('Separacion de canales',fontsize=30)
plt.tight_layout()
plt.show()
No description has been provided for this image
Ahora podemos observar los canales separados, los cuales se notan en un tono gris, sin embrago esto representa la intensidad de cada color en cada capa, por ejemplo en el canal rojo observamos como la parte de la izquierda estΓ‘ marcada en un tono muy claro, esto significa que en esta zona el color rojo tiene una amplia presencia, sin embrago del lado derecho observamos que la imagen se hace mΓ‘s obscura, esto nos dice que en esta zona el color rojo tiene poca participaciΓ³n, asΓ­ podemos analizar cada canal de la imagen, para saber en quΓ© zonas tiene mayor presencia cada color.

CreaciΓ³n de imΓ‘genes
ΒΆ

Hasta este punto tenemos claro que una imagen es un arreglo de matrices, en base a esto surge la siguiente duda, ΒΏsi creamos una matriz, puedo verlo en forma de imagen?, la respuesta es sΓ­, podemos hacer una matriz y leerla de manera que la podemos ver como una imagen.

A continuaciΓ³n lo que vamos a hacer es crear una matriz que tendrΓ‘ el mismo tamaΓ±o que la imagen que estamos trabajando (imagen_redimensionada), despuΓ©s con numpy llenaremos nuestra matriz, podemos llenarla con cualquier valor que queramos, sin embargo en este caso vamos a hacer una matriz llena de ceros lo cual nos mostrarΓ­a una imagen completamente en negro, y mencionar que debemos asignarle a nuestra matriz su tipo de dato en este caso vamos a usar β€˜uint8’, es decir que podemos darle valores a nuestra matriz desde 0 hasta 255.

InΒ [Β ]:
#creamos nuestra matriz y la llenamos con ceros
black = np.zeros(imagen_redimensionada.shape[:2], dtype=np.uint8)

#mostramos nuesta imagen 
plt.imshow(black,cmap='gray')
plt.title('Imagen creada')
plt.axis('off')
plt.show()
No description has been provided for this image

FusiΓ³n de canales
ΒΆ

Una vez visto el tema de separaciΓ³n de canales veremos la manera de unir las matrices de nuevo, sin embrago observaremos como al modificar una capa, podemos observar cambios en nuestra imagen
Apagar canales en una imagen
ΒΆ
En el tema anterior creamos una imagen completamente en negro, esta imagen la vamos a usar como una mΓ‘scara la cual nos servirΓ‘ para mostrar cada canal de nuestra imagen, nuestra imagen en negro funcionara como un apagador, es decir que nuestra imagen tendrΓ‘ 3 canales, pero apagaremos 2 canales y solo mostraremos uno, se podrΓ­a pensar que es lo que vimos al separar los canales de la imagen, sin embargo, podremos notar algunos cambios.
InΒ [Β ]:
'''
para unir canale usamos la funcion merge, en la cual introduciremos las imagenes a unir
en este caso colocaremos el canal en su lugar correspondiente, y en los 2 lugares sobrantes colocaremos 
la imagen que creamos anteriormente (black)
'''
rojo = cv2.merge([r,black,black])
verde = cv2.merge([black,g,black])
azul = cv2.merge([black,black,b])

#mostramos la imagen original y las imagenes generadas al apagar los canales
fig, axs = plt.subplots(3,3,figsize=(20,10))

#imagen original
axs[0,0].imshow(imagen_redimensionada)
axs[0,0].set_title('imagen original')
axs[0,0].axis('off')

#canal en negro (imagen creada para apagar canales)
axs[0,2].imshow(black,cmap='gray')
axs[0,2].set_title('capa en negro creada')
axs[0,2].axis('off')

axs[0,1].axis('off')

#canales separados 
axs[1,0].imshow(r,cmap='gray')
axs[1,0].set_title('canal rojo')
axs[1,0].axis('off')

axs[1,1].imshow(g,cmap='gray')
axs[1,1].set_title('canal verde')
axs[1,1].axis('off')

axs[1,2].imshow(b,cmap='gray')
axs[1,2].set_title('canal azul')
axs[1,2].axis('off')

#imagenes con un canal activo y 2 desactivados
axs[2,0].imshow(rojo)
axs[2,0].set_title('canal rojo activo')
axs[2,0].axis('off')

axs[2,1].imshow(verde)
axs[2,1].set_title('canal verde activo')
axs[2,1].axis('off')

axs[2,2].imshow(azul)
axs[2,2].set_title('canal azul activo')
axs[2,2].axis('off')

plt.tight_layout()
plt.show()
No description has been provided for this image
Como podemos observar al desactivar algunos canales en una imagen nos permite observar de manera clara la presencia de un solo canal en la imagen, al separar los canales solo observamos tonalidades grises, pero cuando desactivamos los canales podemos observar la presencia del canal en la imagen.
Modificar canales
ΒΆ
Antes de unir los canales en una imagen podemos hacer modificaciones a cada canal con la intensiΓ³n de resaltar algunas caracterΓ­sticas en la imagen final , en este caso solo invertiremos la capa roja, y observaremos el cambio que se produce en nuestra imagen.
InΒ [Β ]:
#modificamos un canal para notar las diferencias (en este caso el canal rojo lo invertimos)
r_invert = cv2.bitwise_not(r)
#unimos las capas con la capa modificada
fusion = cv2.merge([r_invert,g,b])

#mostramos la imagen original y la imagen modificada
fig, axs = plt.subplots(1,2,figsize=(10,10))

#imagen original
axs[0].imshow(imagen_redimensionada)
axs[0].set_title('imagen original')
axs[0].axis('off')

axs[1].imshow(fusion)
axs[1].set_title('imagen modificada')
axs[1].axis('off')

plt.tight_layout()
plt.show()
No description has been provided for this image
Podemos observar como al invertir el color rojo hicimos que los elementos que naturalmente son rojos cambien su color, mientras que aquellos elementos donde la presencia de este color no era significativa cambiaron su color a un tono mas rojizo.

EstadΓ­stica de pixeles
ΒΆ

Este apartado se centra en hacer operaciones estadΓ­sticas con los valores de los pixeles, con la intenciΓ³n de hacer algunas interpretaciones de nuestra imagen, cada operaciΓ³n tiene una finalidad que se desglosara a continuaciΓ³n.

MΓ­nimo y mΓ‘ximo en pixeles
ΒΆ

Este punto es sencillo consiste en hallar aquellos pixeles de la imagen que tengan el valor mΓ‘s pequeΓ±o y el mΓ‘s alto, de igual manera hallaremos su ubicaciΓ³n en la imagen, esto se hace para conocer en quΓ© punto tenemos colores con mayor intensidad o en su caso con menor intensidad.

En nuestro ejemplo hallaremos el valor de los pixeles con mayor y menor intensidad en cada canal de una imagen, y hallaremos su ubicaciΓ³n con la intensiΓ³n de saber en quΓ© punto nuestro canal esta mΓ‘s concentrado.

InΒ [Β ]:
#hallamos el valor minimo, maximo y su ubicacion, respecto a cada canal
minvalr,maxvalr,minlocr,maxlocr = cv2.minMaxLoc(r)
minvalg,maxvalg,minlocg,maxlocg = cv2.minMaxLoc(g)
minvalb,maxvalb,minlocb,maxlocb = cv2.minMaxLoc(b)

#mostramos en consola los valores hallados
print(f'En la capa roja el valor minimo es:{minvalr} ubicado en:{minlocr}, el valor maximo es:{maxvalr} ubicado en{maxlocr}')
print(f'En la capa verde el valor minimo es:{minvalg} ubicado en:{minlocg}, el valor maximo es:{maxvalg} ubicado en{maxlocg}')
print(f'En la capa azul el valor minimo es:{minvalb} ubicado en:{minlocb}, el valor maximo es:{maxvalb} ubicado en{maxlocb}')
En la capa roja el valor minimo es:0.0 ubicado en:(283, 67), el valor maximo es:255.0 ubicado en(61, 0)
En la capa verde el valor minimo es:0.0 ubicado en:(2, 2), el valor maximo es:255.0 ubicado en(122, 0)
En la capa azul el valor minimo es:0.0 ubicado en:(23, 0), el valor maximo es:255.0 ubicado en(248, 0)
InΒ [Β ]:
#mostramos los canales 
fig, axs = plt.subplots(1,3,figsize=(10,5))

#imagen original
axs[0].imshow(rojo)
axs[0].set_title('canal rojo')

axs[1].imshow(verde)
axs[1].set_title('canal verde')

axs[2].imshow(azul)
axs[2].set_title('canal azul')

plt.tight_layout()
plt.show()

            
No description has been provided for this image
Ahora que mostramos los canales con sus ejes, podemos buscar los valores hallados y comprobar si estos valores corresponden a pixeles mΓ­nimos o mΓ‘ximos.

Media en pixeles
ΒΆ

Hallar la media de los pixeles en una imagen nos permite saber que tan buena es la iluminaciΓ³n en nuestra imagen y tener una nociΓ³n de en quΓ© valores rondan los pixeles de nuestra imagen.
InΒ [Β ]:
#hallamos la media de los pixeles en cada capa
mediar = np.mean(r)
mediag = np.mean(g)
mediab = np.mean(b)

#hallamos la media de los pixeles en la imagen RGB
media = np.mean(imagen_redimensionada)

#imorimimos los valores obtenidos 
print(f'La media de los pixeles en la capa roja es: {mediar}')
print(f'La media de los pixeles en la capa verde es: {mediag}')
print(f'La media de los pixeles en la capa azul es: {mediab}')
print(f'La media de los pixeles en la imagen RGB: {media}')
La media de los pixeles en la capa roja es: 188.3379006159307
La media de los pixeles en la capa verde es: 134.59475153982672
La media de los pixeles en la capa azul es: 62.295725023488885
La media de los pixeles en la imagen RGB: 128.40945905974877

DesviaciΓ³n estΓ‘ndar en pixeles
ΒΆ

La desviaciΓ³n estΓ‘ndar nos permite saber que tanta es la variaciΓ³n entre los pixeles de nuestra imagen, esto nos permite conocer si nuestra imagen tiene una definiciΓ³n alta, mientras mayor sea la variaciΓ³n tendremos bordes mΓ‘s definidos, lo cual nos permitirΓ‘ identificar objetos en la misma.
InΒ [Β ]:
#calculamos la desviacion estandar en cada canal
stdr = np.std(r)
stdg = np.std(g)
stdb = np.std(b)
std = np.std(imagen_redimensionada)

#mostramos los valores hallados 
print(f'La desviacion estandar en la capa roja es: {stdr}')
print(f'La desviacion estandar en la capa verde es: {stdg}')
print(f'La desviacion estandar en la capa azul es: {stdb}')
print(f'La desviacion estandar en la imagen RGB es: {std}')
La desviacion estandar en la capa roja es: 74.11144545370038
La desviacion estandar en la capa verde es: 83.7586355565115
La desviacion estandar en la capa azul es: 76.83191512276828
La desviacion estandar en la imagen RGB es: 93.8293847675871

Acceso y manipulaciΓ³n de pixeles
ΒΆ

Podemos acceder a los valores de los pixeles de nuestra imagen, lo cual nos permite tener informaciΓ³n mΓ‘s detallada sobre nuestra imagen, de igual manera podemos realizar modificaciones a nuestra imagen pixel por pixel, a continuaciΓ³n, se muestra que acciones podemos realizar mediante el acceso a los pixeles de nuestra imagen.

Valores RGB en los pixeles
ΒΆ

Podemos hallar los valores RGB de un pΓ­xel de manera sencilla, solo debemos saber las coordenadas del pΓ­xel al que queremos acceder.
InΒ [Β ]:
#accedenos al pixel de nuestro interes, en este caso accedemos al elemento [0,0] de nuestra imagen
pixel_value = imagen_redimensionada[0,0]

#mostramos el valor de nuestro pixel
print(F'El valor del pixel [0,0] es: {pixel_value}')
El valor del pixel [0,0] es: [246 212 198]
En este caso accedimos a un solo pixel, sin embargo podemos acceder tambiean a un rango de pixeles.
InΒ [Β ]:
#definimos un rango en este caso sera toda la columna 0
range_value = imagen_redimensionada[:,0]

#mostramos los valores 
print(f'Los valores de los pixeles en la columna 0 son: {range_value}')
Los valores de los pixeles en la columna 0 son: [[246 212 198]
 [242 208 204]
 [244 207 207]
 [244 207 200]
 [241 210 207]
 [253 218 211]
 [250 210 205]
 [251 213 213]
 [251 208 207]
 [244 210 206]
 [250 218 211]
 [253 211 205]
 [250 213 207]
 [248 209 204]
 [247 204 203]
 [251 208 208]
 [250 209 207]
 [250 209 207]
 [250 209 207]
 [250 209 207]
 [251 211 205]
 [254 212 207]
 [255 214 208]
 [251 211 205]
 [252 209 208]
 [251 208 208]
 [250 206 207]
 [249 205 206]
 [248 212 208]
 [244 209 205]
 [251 212 209]
 [249 207 205]
 [243 208 199]
 [255 207 204]
 [249 216 210]
 [252 213 210]
 [247 213 207]
 [246 205 201]
 [253 213 211]
 [250 207 204]
 [242 205 201]
 [244 206 201]
 [248 209 205]
 [247 206 203]
 [252 211 208]
 [247 207 204]
 [246 206 203]
 [249 209 207]
 [248 211 209]
 [248 208 200]
 [249 208 200]
 [249 212 210]
 [248 207 204]
 [249 209 205]
 [248 210 206]
 [247 210 205]
 [254 206 210]
 [247 210 198]
 [244 233 231]
 [245 254 249]
 [248 249 252]
 [249 234 238]
 [236 201 190]
 [244 204 204]
 [242 206 204]
 [238 208 203]
 [239 209 201]
 [245 207 200]
 [248 214 205]
 [245 220 216]
 [245 217 214]
 [249 206 203]
 [248 208 208]
 [247 207 207]
 [244 208 206]
 [241 207 205]
 [247 208 205]
 [247 208 205]
 [247 208 205]
 [247 208 205]
 [247 207 206]
 [235 208 202]
 [246 205 201]
 [242 207 200]
 [247 228 226]
 [241 225 218]
 [254 205 204]
 [247 206 202]
 [248 208 205]
 [250 209 205]
 [253 209 206]
 [254 208 206]
 [252 209 202]
 [251 219 208]
 [252 225 212]
 [251 224 209]
 [253 224 225]
 [251 228 226]
 [250 229 228]
 [247 216 215]
 [243 216 216]
 [246 204 207]
 [247 212 211]
 [237 206 201]
 [240 204 206]
 [248 205 203]
 [255 216 211]
 [252 224 221]
 [251 223 223]
 [248 224 222]
 [250 223 219]
 [252 218 215]
 [247 205 199]
 [250 203 201]
 [250 205 204]
 [248 222 215]
 [232 208 203]
 [232 211 205]
 [236 218 210]
 [232 216 207]
 [230 214 196]
 [251 252 253]
 [252 254 254]
 [253 250 252]
 [249 215 214]
 [238 207 196]
 [248 199 207]
 [247 210 201]
 [248 209 206]
 [247 208 206]
 [247 208 205]
 [246 208 205]
 [240 206 205]
 [243 208 208]
 [245 204 209]
 [224 209 206]
 [220 203 198]
 [235 202 208]
 [218 207 206]
 [228 208 210]
 [220 204 208]
 [217 207 208]
 [222 206 207]
 [220 206 206]
 [234 211 205]
 [238 210 206]
 [241 208 206]
 [243 207 207]
 [247 208 208]
 [246 208 207]
 [243 208 206]
 [241 208 206]
 [242 208 206]
 [242 208 206]
 [242 208 206]
 [242 208 206]
 [242 205 206]
 [235 206 206]
 [228 205 204]
 [224 203 203]
 [233 203 208]
 [221 207 205]
 [230 208 208]
 [221 209 204]
 [206 218 208]
 [214 213 200]
 [211 206 210]
 [208 208 203]
 [209 217 210]
 [210 215 200]
 [221 200 206]
 [224 210 205]
 [223 205 205]
 [205 213 205]
 [215 210 208]
 [209 207 202]
 [211 205 207]
 [213 208 210]
 [207 204 205]
 [210 209 209]
 [206 207 206]
 [208 210 204]
 [211 209 204]
 [239 233 237]
 [250 249 245]
 [254 252 249]
 [239 229 231]
 [252 201 204]
 [248 209 204]
 [247 209 207]
 [254 199 193]
 [243 201 204]
 [247 201 207]
 [250 205 205]
 [243 209 211]
 [226 205 211]
 [207 208 201]
 [207 208 206]
 [217 206 208]
 [222 217 213]
 [238 242 204]
 [237 239 187]
 [240 231 212]
 [233 202 193]
 [243 213 212]
 [249 200 205]
 [233 213 207]
 [241 216 211]
 [248 207 207]
 [245 212 214]
 [233 204 203]
 [235 201 190]
 [242 205 201]
 [247 204 204]
 [237 209 205]
 [243 210 209]
 [239 216 202]
 [234 200 196]
 [240 206 209]
 [242 219 214]
 [244 205 202]
 [244 209 205]
 [240 208 203]
 [237 209 203]
 [240 207 200]
 [242 207 202]
 [241 210 206]
 [234 205 202]
 [241 211 205]
 [241 207 206]
 [236 205 207]
 [225 207 207]
 [237 206 206]
 [236 197 206]
 [219 209 209]
 [218 214 206]
 [235 203 205]
 [234 214 206]
 [242 206 212]
 [245 202 201]
 [242 229 228]
 [253 207 211]
 [242 198 197]
 [247 201 203]
 [241 198 191]
 [249 214 226]
 [251 251 252]
 [247 249 255]]

ManipulaciΓ³n de pixeles en un rango (ROI)
ΒΆ

Ya vimos como acceder a los valores de los pixeles, sin embargo, podemos modificar sus valores de la siguiente manera.
InΒ [Β ]:
#creamo una copia de la imagen en la que efectuaremos cambios 
img_manipulada = imagen_redimensionada.copy()

#definimos un rango a manipular 
rangol = img_manipulada[185:246,62:123]

#modificamos el valor de los piixeles en el rango, en este caso les asignaremos solo un color azul
rangol[:] = [0,0,255]

#mostramos la imagen
plt.imshow(img_manipulada)
plt.title('imagen con pixeles modificados')
plt.axis('off')
plt.show()
No description has been provided for this image
Podemos observar como modificamos un cuadro de la imagen, al cual le asignamos un color azul completamente.

Manipulacion de todos los pixeles
ΒΆ

Hemos modificado pixeles de manera individual o en rangos, sin embargo, podemos aplicar modificaciones a todos los pixeles, a continuaciΓ³n se muestran algunas operaciones donde se modifican los valores de todos los pixeles.
Inversion de colores
ΒΆ
Podemos invertir los valores de nuestra imagen de manera sencilla, como se muestra a continuaciΓ³n.
InΒ [Β ]:
#INVERTIMOS LOS COLORES DE NUESTRA IMAGEN
imagen_invertida = cv2.bitwise_not(imagen_redimensionada)

#mostramos la imagen
plt.imshow(imagen_invertida)
plt.title('Imagen con canales invertidos')
plt.axis('off')
plt.show()
No description has been provided for this image
Imagen en escala de grises
ΒΆ
Podemos cambiar los valores de nuestros pixeles haciendo que dejen de tener 3 valores(RGB) haciendo que tengan un solo valor de intensidad, dejando como resultado una imagen en escala de grises.
InΒ [Β ]:
#CONVERTIMOS NUESTRA IMAGEN EN ESCALA DE GRISES
imagen_gray = cv2.cvtColor(imagen_redimensionada,cv2.COLOR_BGR2GRAY)

#MOSTRAMOS NUESTRA IMAGEN
plt.imshow(imagen_gray,cmap='gray')
plt.title('Imagen en escala de grises')
plt.axis('off')
plt.show()
No description has been provided for this image
En este caso hemos eliminado los canales de nuestra imagen por lo que ahora solo consta de uno, que asigna una intensidad a nuestra imagen con valores entre 0 a 256.

REFERENCIAS
ΒΆ